using System;
using System.ComponentModel;
using System.Runtime.Serialization;

namespace SharpGL.SceneGraph.Assets
{
    /// <summary>
    /// The display list class basicly wraps an OpenGL display list, making them easier
    /// to manage. Remember this class is completely OpenGL dependant. In time this class
    /// will derive from the IOpenGLDependant interface.
    /// </summary>
    [TypeConverter(typeof(System.ComponentModel.ExpandableObjectConverter))]
    public class DisplayList : IDisposable, IHasOpenGLContext, INotifyPropertyChanged, INotifyContextChanged
    {
        #region Public Types

        public enum DisplayListMode
        {
            Compile = (int)OpenGL.GL_COMPILE,
            CompileAndExecute = (int)OpenGL.GL_COMPILE_AND_EXECUTE,
        }

        #endregion Public Types

        #region Private Fields

        /// <summary>
        /// Current OpenGL context.
        /// </summary>
        private OpenGL currentOpenGLContext = null;

        /// <summary>
        /// OpenGL object handle.
        /// </summary>
        private uint list = 0;

        #endregion Private Fields

        #region Protected Methods

        /// <summary>
        /// Call this function to do the ContextChanged event.
        /// </summary>
        protected void NotifyContextChanged(object sender, ContextChangeAction action)
        {
            var handler = this.ContextChanged;
            if (handler != null)
                handler(sender, new ContextChangeEventArgs(action));
        }

        /// <summary>
        /// Call this function to do the PropertyChanged event.
        /// </summary>
        protected void NotifyPropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion Protected Methods

        #region Public Constructors/Destructor

        public DisplayList()
        {
        }

        ~DisplayList()
        {
            Destroy();
        }

        #endregion Public Constructors/Destructor

        #region Public Methods

        /// <summary>
        /// This function makes the display list.
        /// </summary>
        /// <param name="gl">OpenGL</param>
        /// <returns>True if the list was successfully created.</returns>
        public virtual bool Create(OpenGL gl)
        {
            if (!OpenGL.Equals(this.currentOpenGLContext, gl))
            {
                if (OpenGL.IsValidContext(this.currentOpenGLContext))
                    throw new Exception("Can't create display list in context '" +
                                        (OpenGL.IsValidContext(gl) ? gl.Renderer : "Null") +
                                        "' - it is already created in '" +
                                        (OpenGL.IsValidContext(this.currentOpenGLContext) ? this.currentOpenGLContext.Renderer : "Null") +
                                        "' OpenGL context.");

                if (OpenGL.IsValidContext(gl))
                {
                    this.currentOpenGLContext = gl;

                    //	Generate one list.
                    this.list = gl.GenLists(1);

                    NotifyPropertyChanged("List");
                    NotifyContextChanged(this, ContextChangeAction.CreateInContext);
                }
            }

            return true;
        }

        public virtual void Destroy()
        {
            if (OpenGL.IsValidContext(this.currentOpenGLContext))
            {
                NotifyContextChanged(this, ContextChangeAction.DestroyInContext);

                //	Destroy the list object.
                this.currentOpenGLContext.DeleteLists(this.list, 1);
                this.list = 0;

                NotifyPropertyChanged("List");

                this.currentOpenGLContext = null;
            }
        }

        /// <summary>
        /// Create in the context of the supplied OpenGL instance.
        /// </summary>
        /// <param name="gl">The OpenGL instance.</param>
        public virtual void CreateInContext(OpenGL gl)
        {
            Create(gl);
        }

        /// <summary>
        /// Destroy in the current OpenGL instance.
        /// </summary>
        public virtual void DestroyInContext()
        {
            Destroy();
        }

        /// <summary>
        /// This function makes the display list.
        /// </summary>
        /// <param name="mode">The mode, compile or compile and execute.</param>
        public virtual void Begin(DisplayListMode mode)
        {
            if (OpenGL.IsValidContext(this.currentOpenGLContext))
            {
                //	Start the list.
                this.currentOpenGLContext.NewList(this.list, (uint)mode);
            }
        }

        public void Dispose()
        {
            Destroy();

            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// This function ends the compilation of a list.
        /// </summary>
        public virtual void End()
        {
            if (OpenGL.IsValidContext(this.currentOpenGLContext))
            {
                //	This function ends the display list
                this.currentOpenGLContext.EndList();
            }
        }

        public virtual void Call()
        {
            if (OpenGL.IsValidContext(this.currentOpenGLContext))
            {
                this.currentOpenGLContext.CallList(list);
            }
        }

        public virtual bool IsList()
        {
            if (OpenGL.IsValidContext(this.currentOpenGLContext))
            {
                //	Is the list a proper display list?
                return (this.currentOpenGLContext.IsList(this.list) == OpenGL.GL_TRUE);
            }

            return false;
        }

        public static bool IsList(DisplayList displayList)
        {
            if (displayList != null)
            {
                //	Is the specified list a proper display list?
                return displayList.IsList();
            }

            return false;
        }

        #endregion Public Methods

        #region Public Events

        [Description("Called when OpenGL context of effect has been changed."), Category("SharpGL")]
        public event ContextChangeEventHandler ContextChanged;

        [Description("Called when properties has been changed."), Category("SharpGL")]
        public event PropertyChangedEventHandler PropertyChanged;

        #endregion Public Events

        #region Public Properties

        /// <summary>
        /// Current OpenGL context
        /// </summary>
        [Browsable(false)]
        public OpenGL CurrentOpenGLContext
        {
            get { return currentOpenGLContext; }
        }

        /// <summary>
        /// OpenGL object handle.
        /// </summary>
        public uint List
        {
            get { return list; }
        }

        #endregion Public Properties
    }
}
